//
//  Block.h
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 14/12/06.
//  Copyright 2006 Ofri Wolfus. All rights reserved.
//

#import <HigherOrderMessaging/DPMacros.h>
#include <alloca.h>


typedef struct dp_block_data *Block;

// Allocates and returns a new block structure.
// You should never use this function directly.
DP_EXTERN Block block_init(Block b);
//DP_EXTERN Block block_create();

// Frees the memory used by the passed block.
// Call this function when you're done working with your block (which
// will usually be inside the function that takes the block).
//DP_EXTERN void block_free(Block b);

// Executes the code contained by the block, and reinvokes the calling function
// with the same block.
DP_EXTERN_INLINE void block_process(Block b);

// Sets/gets private info from a given block.
// You can use this info to hold data for your iteration methods/functions.
// Keys must be ObjC objects or CFType instances which will be copied and released
// when the block is freed. Values can be any pointer type
// (which will not be retained/released).
DP_EXTERN_INLINE void block_set_value_for_key(Block b, void *val, id key);
DP_EXTERN_INLINE void * block_get_value_for_key(Block b, id key);

// Returns whether a given block is initialized.
// block_process() automatically marks the block as initialized the first
// time it is being called with a given block.
DP_EXTERN_INLINE BOOL block_is_initialized(Block b);

DP_EXTERN_INLINE size_t block_get_size();


/*!
 * @abstract Creates and returns a block sturcture.
 * @discussion The use of this macro must be nested inside a function/method call
 * as one of the arguments. Creating blocks outside of this context will result in
 * an undefined behaviour.
 * A block definition goes like this:
	<code>DPBlock(int i = 3; id o = nil; begin: NSLog(@"%d", i);)</code>
 * Every block definition begins with variable definition(s) followed by the keyword "begin:",
 * and then the content of the block. The variable definition(s) may be omitted, in which case
 * the definition will look like this: <code>DPBlock(begin: ...)</code>.
 * Variables defined in the content section of a block (that is, after the "begin:" keyword)
 * will not preserve their values between invocations of the block.
 * Every block defines a hidden variable of type <code>id</code> named "each".
 * Blocks that are passed to iteration methods/functions modify the content of this variable
 * during the iteration, before the block's content is executed, so you can use blocks perform
 * actions on objects of collections like this:
	<code>[someArray foreach:DPBlock(begin: NSLog(@"%@", each)];</code>
 * Note: The last statement of the block's content may omit the semicolon at the end.
 */
#define Block(statements...) \
({ \
	__label__ __block_begin; \
	Block __block = block_init(alloca(block_get_size())); \
	__block_begin: \
		if (setjmp(*((jmp_buf*)__block))) { \
			id	each = (id)block_get_value_for_key(__block, @"each"); \
			statements; \
			goto __block_begin; \
		} \
		__block; \
})
